﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Threading;
using System.Diagnostics;

namespace WindowsFormsApplication1
{
    public partial class FrmHook : Form
    {
        //Form Class
        const string FRM_CLASSNAME = "TXGuiFoundation";

        const int MAPVK_VK_TO_VSC = 0x0000;
        //mouse msg
        const int WM_MOUSEMOVE = 0x0200;

        const int WM_LBUTTONDOWN = 0x0201;
        const int WM_LBUTTONUP = 0x0202;
        const int WM_LBUTTONDBLCLK = 0x0203;

        const int WM_RBUTTONDOWN = 0x0204;
        const int WM_RBUTTONUP = 0x0205;
        const int WM_RBUTTONDBLCLK = 0x0206;

        const int WM_MBUTTONDOWN = 0x0207;
        const int WM_MBUTTONUP = 0x0208;
        const int WM_MBUTTONDBLCLK = 0x0209;

        const int WM_MOUSEWHEEL = 0x020A;
        const int WM_MOUSEHWHEEL = 0x020E;

        //keyboard msg
        const int WM_KEYDOWN = 0x0100;
        const int WM_KEYUP = 0x0101;
        const int WM_CHAR = 0x0102;
        const int WM_DEADCHAR = 0x0103;
        const int WM_SYSKEYDOWN = 0x0104;
        const int WM_SYSKEYUP = 0x0105;
        const int WM_SYSCHAR = 0x0106;
        const int WM_SYSDEADCHAR = 0x0107;

        const int SW_MAXIMIZE = 0x0003;

        public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
        public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);

        static int hMouseHook;
        static int hKeyboardHook;

        //Declare the mouse hook constant.
        //For other hook types, you can obtain these values from Winuser.h in the Microsoft SDK.
        public const int WH_MOUSE = 7;
        
        /// <summary>
        /// Windows NT/2000/XP: Installs a hook procedure that monitors low-level mouse input events.
        /// </summary>
        private const int WH_MOUSE_LL = 14;
        /// <summary>
        /// Windows NT/2000/XP: Installs a hook procedure that monitors low-level keyboard  input events.
        /// </summary>
        private const int WH_KEYBOARD_LL = 13;

        //Declare MouseHookProcedure as a HookProc type.
        HookProc MouseHookProcedure;
        HookProc KeyboardHookProcedure;
        EnumWindowsProc WindowProcedure;

        //Declare the wrapper managed POINT class.
        [StructLayout(LayoutKind.Sequential)]
        public class POINT
        {
            public int x;
            public int y;
        }

        //Declare the wrapper managed MouseHookStruct class.
        [StructLayout(LayoutKind.Sequential)]
        public class MouseHookStruct
        {
            public POINT pt;
            public int hwnd;
            public int wHitTestCode;
            public int dwExtraInfo;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct KeyboardHookStruct
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }


        //This is the Import for the SetWindowsHookEx function.
        //Use this function to install a thread-specific hook.
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        //This is the Import for the UnhookWindowsHookEx function.
        //Call this function to uninstall the hook.
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

        //This is the Import for the CallNextHookEx function.
        //Use this function to pass the hook information to the next hook procedure in chain.
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetModuleHandle(string name);

        [DllImport("user32.dll", EntryPoint = "keybd_event")]
        public static extern void keybd_event(
            byte bVk,    //虚拟键值
            byte bScan,// 一般为0
            int dwFlags,  //这里是整数类型  0 为按下，2为释放
            int dwExtraInfo  //这里是整数类型 一般情况下设成为 0
        );

        [DllImport("user32")]
        public static extern int EnumWindows(EnumWindowsProc lpfn, int lParam);
        [DllImport("user32")]
        public static extern int EnumChildWindows(IntPtr hWndParent, EnumWindowsProc lpfn, int lParam);
        [DllImport("User32.dll")]
        private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        [DllImport("User32.dll")]
        private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName);
        [DllImport("User32.Dll")]
        public static extern void GetWindowText(int h, StringBuilder s, int nMaxCount);
        [DllImport("User32.Dll")]
        public static extern void GetClassName(int h, StringBuilder s, int nMaxCount);
        [DllImport("user32.dll", EntryPoint = "ShowWindow", SetLastError = true)]
        public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll", EntryPoint = "SendMessage")]
        public static extern bool SendMessage(IntPtr hWnd, Int32 msg, uint wParam, uint lParam);
        [DllImport("user32.dll", EntryPoint = "PostMessage")]
        public static extern bool PostMessage(IntPtr hWnd, Int32 msg, uint wParam, uint lParam);
        [DllImport("user32.dll", EntryPoint = "MapVirtualKey")]
        public static extern uint MapVirtualKey(uint uCode, uint uMapType);

        public FrmHook()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (hMouseHook == 0 || hKeyboardHook == 0)
            {
                MouseHookProcedure = new HookProc(MouseHookProc);
                KeyboardHookProcedure = new HookProc(KeyboardHookProc);
                //install hook
                hMouseHook = SetWindowsHookEx(
                    WH_MOUSE_LL,　//原来是WH_MOUSE
                    MouseHookProcedure,
                    GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName),
                    0);
                if (hMouseHook == 0)
                    Console.WriteLine("Mouse hook failed");

                //int code = Marshal.GetLastWin32Error();
                //install hook
                hKeyboardHook = SetWindowsHookEx(
                    WH_KEYBOARD_LL, //原来是WH_KEYBORAD
                    KeyboardHookProcedure,
                    GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName),
                    0);
                //code = Marshal.GetLastWin32Error();
                if (hKeyboardHook == 0)
                    Console.WriteLine("Keyboard hook failed");
            }
            else
            {
                bool rslt = UnhookWindowsHookEx(hMouseHook);
                if (rslt)
                    hMouseHook = 0;
                else
                    Console.WriteLine("Mouse unhook failed");
                rslt = UnhookWindowsHookEx(hKeyboardHook);
                if (rslt)
                    hKeyboardHook = 0;
                else
                    Console.WriteLine("Keyboard unhook failed");
            }
        }

        private int MouseHookProc(int nCode, Int32 wParam, IntPtr lParam)
        {
            if (nCode >= 0)
            {
                MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
                int i = (int)wParam;
                switch (i)
                {
                    case WM_LBUTTONDOWN:
                        printMouse("WM_LBUTTONDOWN");
                        break;
                    case WM_LBUTTONUP:
                        printMouse("WM_LBUTTONUP");
                        break;
                    case WM_LBUTTONDBLCLK:
                        printMouse("WM_LBUTTONDBLCLK");
                        break;
                    case WM_RBUTTONDOWN:
                        printMouse("WM_RBUTTONDOWN");
                        break;
                    case WM_RBUTTONUP:
                        printMouse("WM_RBUTTONUP");
                        break;
                    case WM_RBUTTONDBLCLK:
                        printMouse("WM_RBUTTONDBLCLK");
                        break;
                    case WM_MOUSEWHEEL:
                        //If the message is WM_MOUSEWHEEL, the high-order word of mouseData member is the wheel delta. 
                        //One wheel click is defined as WHEEL_DELTA, which is 120. 
                        //(value >> 16) & 0xffff; retrieves the high-order word from the given 32-bit value
                        //mouseDelta = (short)((mouseHookStruct.mouseData >> 16) & 0xffff);
                        //TODO: X BUTTONS (I havent them so was unable to test)
                        //If the message is WM_XBUTTONDOWN, WM_XBUTTONUP, WM_XBUTTONDBLCLK, WM_NCXBUTTONDOWN, WM_NCXBUTTONUP, 
                        //or WM_NCXBUTTONDBLCLK, the high-order word specifies which X button was pressed or released, 
                        //and the low-order word is reserved. This value can be one or more of the following values. 
                        //Otherwise, mouseData is not used. 
                        break;
                }
            }
            return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
        }

        private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
        {
            if (nCode >= 0)
            {
                KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
                Keys key = (Keys)MyKeyboardHookStruct.vkCode;
                switch (wParam)
                {
                    case WM_KEYDOWN:
                        printKeyboard("WM_KEYDOWN:  " + key.ToString());
                        break;
                    case WM_KEYUP:
                        printKeyboard("WM_KEYUP:  " + key.ToString());
                        break;
                    default:
                        printKeyboard("WM:  " + key.ToString());
                        break;
                }

            }
            return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
        }

        private bool WindowsEnumProc(IntPtr hwnd, IntPtr lParam)
        {
            TextBox txt = (TextBox)TextBox.FromHandle(lParam);
            StringBuilder sb = new StringBuilder(1024);
            StringBuilder sbc = new StringBuilder(256);
            GetClassName((int)hwnd, sbc, sbc.Capacity);
            GetWindowText((int)hwnd, sb, sb.Capacity);
            if (sbc.ToString() == "Notepad")
            {
                txt.Text += sb.ToString() + " " + sbc.ToString() + " " + hwnd + "\r\n";
                txt.SelectionStart = txt.Text.Length;
                txt.ScrollToCaret();
            }
            return true;
        }

        private bool ChildEnumProc(IntPtr hwnd, IntPtr lParam)
        {
            TextBox txt = (TextBox)TextBox.FromHandle(lParam);
            StringBuilder sb = new StringBuilder(1024);
            StringBuilder sbc = new StringBuilder(256);
            GetClassName((int)hwnd, sbc, sbc.Capacity);
            GetWindowText((int)hwnd, sb, sb.Capacity);
            txt.Text += sb.ToString() + " " + sbc.ToString() + " " + hwnd + "\r\n";
            txt.SelectionStart = txt.Text.Length;
            txt.ScrollToCaret();
            return true;
        }

        private void printMouse(string str)
        {
            txtMouse.Text += str + "\r\n";
            txtMouse.SelectionStart = txtMouse.Text.Length;
            txtMouse.ScrollToCaret();
        }

        private void printKeyboard(string str)
        {
            txtKey.Text += str + "\r\n";
            txtKey.SelectionStart = txtKey.Text.Length;
            txtKey.ScrollToCaret();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            txtProcess.Text = "";
            WindowProcedure = new EnumWindowsProc(WindowsEnumProc);
            EnumWindows(WindowProcedure, (int)txtProcess.Handle);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            IntPtr hwnd = (IntPtr)0x00160F16;

            //IntPtr hwnd = FindWindow(FRM_CLASSNAME, "大工软件0506");//FindWindow("Notepad", "111.txt - メモ帳");
            //hwnd = FindWindowEx(hwnd, (IntPtr)0, "Edit", null);
            //SendMessage(hwnd, WM_KEYDOWN, (int)Keys.A, 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'T', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'h', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'i', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 's', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, ' ', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'i', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 's', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, ' ', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'a', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, ' ', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 't', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'e', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 's', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 't', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, ' ', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'f', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'o', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'r', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, ' ', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'S', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'e', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'n', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'd', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'M', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'e', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 's', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 's', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'a', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'g', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, 'e', 0x001E0001);
            SendMessage(hwnd, WM_CHAR, '.', 0x001E0001);
            SendMessage(hwnd, WM_KEYDOWN, (int)Keys.Enter, 0x011C0001);
            SendMessage(hwnd, WM_KEYUP, (int)Keys.Enter, 0xC11C0001);
        }

        private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
        {
            Console.WriteLine(e.KeyChar);
        }

        private void textBox1_KeyDown(object sender, KeyEventArgs e)
        {
            Console.WriteLine(e.KeyCode.ToString());
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            return base.ProcessCmdKey(ref msg, keyData);
        }
    }
}
